<?php

class BaseDao extends Base implements IBaseDao {

	protected $dataSource;
	protected $mapping;

	public function __construct($dsn, $username, $password) {
		parent::__construct();
		Logger::debug($this,"Executando o construtor");
		$this->dataSource = new DataSource($dsn, $username, $password);
		$this->mapping = NULL;
	}
	
	public function findById($id = NULL) {
		Logger::debug($this,"Executando o metodo findById");
		$mappingInfo = $this->mapping->getMappingInfo();

		$tableName =  $this->mapping->getTableName();

		$idFieldName = $this->mapping->getIdName();
		
		$query = "SELECT * FROM " . $tableName . " WHERE ";
		
		$param = array();
		$first = true;
		$count = 1;
		foreach ($mappingInfo as $field => $column) {
			if ( in_array($field,$idFieldName) ) {
				if (!$first) {
					$query .= " and ";
				} else {
					$first = false;
				}
				$query .= ($column . "=:" . $field);
				$param[$field] = $id[$count - 1];
				$count = intval($count) + 1;
			}
		}
		
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$statement->execute($param);
		$result = $statement->fetch(PDO::FETCH_ASSOC);
		$obj = $this->makeObject($result);
		return $obj;
	}
	
	public function save($obj) {
		Logger::debug($this,"Executando o metodo save");
		$mappingInfo = $this->mapping->getMappingInfo();

		$tableName =  $this->mapping->getTableName();

		$idFieldName = $this->mapping->getIdName();
		
		if( sizeof($idFieldName) == 1 ) {
			$idColumnName = $mappingInfo[$idFieldName[0]];
			$id = $this->getNextId($idColumnName, $tableName);
			Util::callSetter($obj, $idFieldName[0], $id);	
		} 
				
		$query = "INSERT INTO " . $tableName . " (";
		
		$first = true;
		foreach ($mappingInfo as $field => $column) {
			if (!$first) {
				$query .= ", ";
			} else {
				$first = false;
			}
			
			$query .= $column;
		}
		$query .= ") VALUES (";
		
		$first = true;
		$param = array();
		foreach ($mappingInfo as $field => $column) {
			if (!$first) {
				$query .= ", ";
			} else {
				$first = false;
			}

			$query .= (":" . $field);

			$param[$field] = Util::callGetter($obj, $field);
			
		}
		$query .= ");";
		
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$result = $statement->execute($param);

		return $obj;
	}

	public function update($obj) {
		Logger::debug($this,"Executando o metodo update");
		$mappingInfo = $this->mapping->getMappingInfo();

		$tableName =  $this->mapping->getTableName();
		$idFieldName = $this->mapping->getIdName();
		
		$param = array();
		
		$query = "UPDATE " . $tableName . " SET ";
		
		$first = true;
		foreach ($mappingInfo as $field => $column) {
			if ( !in_array($field,$idFieldName) ) {
				if (!$first) {
					$query .= ", ";
				} else {
					$first = false;
				}
				$query .= ($column . "=:" . $field);
				$param[$field] = Util::callGetter($obj, $field);
			}						
		}
				
		$query .= (" WHERE ");
		
		$first = true;
		foreach ($mappingInfo as $field => $column) {
			if ( in_array($field,$idFieldName) ) {
				if (!$first) {
					$query .= " and ";
				} else {
					$first = false;
				}
				$query .= ($column . "=:" . $field);
				$param[$field] = Util::callGetter($obj, $field);
			}
		}
		
		$query .= ";";

		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$result = $statement->execute($param);
		
		return $obj;
	}
	
	public function remove($obj) {
		Logger::debug($this,"Executando o metodo remove");
		$mappingInfo = $this->mapping->getMappingInfo();
		
		$tableName =  $this->mapping->getTableName();
		$idFieldName = $this->mapping->getIdName();
					
		$query = "DELETE FROM " . $tableName . " WHERE ";
		
		$param = array();
		$first = true;
		foreach ($mappingInfo as $field => $column) {
			if ( in_array($field,$idFieldName) ) {
				if (!$first) {
					$query .= " and ";
				} else {
					$first = false;
				}
				$query .= ($column . "=:" . $field);
				$param[$field] = Util::callGetter($obj, $field);
			}
		}
		
		$query .= ";";
		
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
			
		$result = $statement->execute($param);
		return $result;
	}

	public function findAll($config = null) {
		Logger::debug($this,"Executando o metodo findAll");
		$orderby = $config ? $config["orderby"] : null;
		$pagination = $config ? $config["pagination"] : null;
		
		$tableName =  $this->mapping->getTableName();
		$mappingInfo = $this->mapping->getMappingInfo();
		
		$query = "SELECT * FROM " . $tableName;
		
		if ($orderby) {
			$query .= (" ORDER BY " . $mappingInfo[$orderby] . " ASC");
		}
		
		if ($pagination) {
			$query .= (" LIMIT " . $pagination["start"] . ", " . $pagination["size"]);
		}
		$query .= ";";

		Logger::debug($this,"Query : " . $query);
		
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$statement->execute();

		$resultArray = $statement->fetchAll(PDO::FETCH_ASSOC);
			
		$objArray = array();

		foreach ($resultArray as $result) {
			$objArray[] = $this->makeObject($result);
		}

		return $objArray;
	}

	public function query($criteria) {
		Logger::debug($this,"Executando o metodo query");
		$tableName =  $this->mapping->getTableName();
		$mappingInfo = $this->mapping->getMappingInfo();

		$query = "SELECT * FROM " . $tableName . " WHERE ";
		
		$first = true;
		foreach ($criteria as $key => $value) {
			if (!$first) {
				$query .= " AND ";
			} else {
				$first = false;
			}
				
			$query .= ($mappingInfo[$key] . "=:" .$key);
		}

		$query .= ";";
		
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$statement->execute($criteria);
			
		$resultArray = $statement->fetchAll(PDO::FETCH_ASSOC);
			
		$objArray = array();
		
		foreach ($resultArray as $result) {
			$obj = $this->makeObject($result);
			$objArray[] = $obj;
		}
		
		Logger::debug($this,"Metodo query executado com sucesso : $query");
		return $objArray;
	}

	public function execute($sql) {
		Logger::debug($this,"Executando o metodo execute");			
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($sql);
		$statement->execute();

		$resultArray = $statement->fetchAll(PDO::FETCH_ASSOC);
		
		return $resultArray;
	}
	
	protected function getNextId($idColumnName, $tableName) {
		Logger::debug($this,"Executando o metodo getNextId");
		$query = "SELECT MAX(" . $idColumnName . ") FROM " . $tableName . ";";
			
		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$statement->execute();
		$result = $statement->fetch(PDO::FETCH_NUM);
			
		return $result[0] + 1;
	}

	protected function makeObject($result) {
		Logger::debug($this,"Executando o metodo makeObject");
		$mappingInfo = $this->mapping->getMappingInfo();
		$className = $this->mapping->getClassName();
		
		$obj = NULL;
		if ($result) {
			$obj = new $className();
			foreach ($mappingInfo as $key => $value) {
				$setterMethodName = "set" . ucfirst($key);
				$obj->$setterMethodName($result[$value]);
			}
				
		}
		return $obj;
	}
	
	public function count() {
		Logger::debug($this,"Executando o metodo count");
		$tableName =  $this->mapping->getTableName();
		$query = "SELECT COUNT(*) FROM " . $tableName . ";";

		$pdo = $this->dataSource->getPdo();
		$statement = $pdo->prepare($query);
		$statement->execute();
		$result = $statement->fetch(PDO::FETCH_NUM);
			
		return $result[0];
	}
	
	public function findByExample($entity) {
		Logger::debug($this,"Executando o metodo findByExample");
		$mappingInfo = $this->mapping->getMappingInfo();
		$criteria = array();
		
		foreach ($mappingInfo as $key => $value) {
			$obj = Util::callGetter($entity, $key);
			
			if( $obj != null && $obj != "" ) {
				$criteria[$key] = $obj;
			}						
		}
		
		return $this->query($criteria);
	}
}
?>
